// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#pragma once

#include <pollux/common/compression/compression.h>
#include <pollux/dwio/common/seekable_input_stream.h>
#include <pollux/dwio/common/encryption/encryption.h>

namespace kumo::pollux::dwio::common::compression {
    class Compressor {
    public:
        // zlib window bits determine the size of history buffer. If the value is
        // negative, then no zlib header/trailer or a check value will be added and
        // ABS(windowBits) will determine the buffer size.
        // https://zlib.net/manual.html
        static constexpr int DWRF_ORC_ZLIB_WINDOW_BITS = -15;
        static constexpr int PARQUET_ZLIB_WINDOW_BITS = 15;

        explicit Compressor(int32_t level) : level_{level} {
        }

        virtual ~Compressor() = default;

        virtual uint64_t compress(const void *src, void *dest, uint64_t length) = 0;

    protected:
        int32_t level_;
    };

    class Decompressor {
    public:
        explicit Decompressor(uint64_t blockSize, const std::string &streamDebugInfo)
            : blockSize_{static_cast<int64_t>(blockSize)},
              streamDebugInfo_{streamDebugInfo} {
        }

        virtual ~Decompressor() = default;

        virtual std::pair<int64_t, bool /* Is the size exact? */>
        getDecompressedLength(const char * /* src */, uint64_t /* srcLength */) const {
            return {blockSize_, false};
        }

        virtual uint64_t decompress(
            const char *src,
            uint64_t srcLength,
            char *dest,
            uint64_t destLength) = 0;

    protected:
        int64_t blockSize_;
        const std::string streamDebugInfo_;
    };

    struct CompressionOptions {
        /// Format specific compression/decompression options
        union Format {
            struct {
                /// Window bits determines the history buffer size and whether
                /// header/trailer is added to the compression block.
                int windowBits;
                /// Compression level determines the compression ratio. Zlib supports
                /// values ranging from 0 (no compression) to 9 (max compression)
                int32_t compressionLevel;
            } zlib;

            struct {
                int32_t compressionLevel;
            } zstd;

            struct {
                bool isHadoopFrameFormat;
            } lz4_lzo;
        } format;

        uint32_t compressionThreshold;
    };

    /**
     * Create a decompressor for the given compression kind.
     * @param kind The compression type to implement
     * @param input The input stream that is the underlying source
     * @param bufferSize The maximum size of the buffer
     * @param pool The memory pool
     * @param options The compression options to use
     * @param useRawDecompression Specify whether to perform raw decompression
     * @param compressedLength The compressed block length for raw decompression
     */
    std::unique_ptr<dwio::common::SeekableInputStream> createDecompressor(
        kumo::pollux::common::CompressionKind kind,
        std::unique_ptr<dwio::common::SeekableInputStream> input,
        uint64_t bufferSize,
        memory::MemoryPool &pool,
        const CompressionOptions &options,
        const std::string &streamDebugInfo,
        const dwio::common::encryption::Decrypter *decryptr = nullptr,
        bool useRawDecompression = false,
        size_t compressedLength = 0);

    /**
     * Create a compressor for the given compression kind.
     * @param kind The compression type to implement
     * @param options The compression options to use
     */
    std::unique_ptr<Compressor> createCompressor(
        kumo::pollux::common::CompressionKind kind,
        const CompressionOptions &options);
} // namespace kumo::pollux::dwio::common::compression
